Cash - CS50x 2023
入门
打开 VS Code。
首先点击你的终端窗口,然后执行 cd
命令。你应该看到类似下面的命令行提示符。
点击终端窗口,然后执行
wget https://cdn.cs50.net/2022/fall/psets/1/cash.zip
然后按 Enter 键,以下载一个名为 cash.zip
的 ZIP 文件到你的代码空间 (codespace) 中。注意不要忽略 wget
和后面的 URL 之间的空格,或者任何其他字符!
现在执行
来创建一个名为 cash
的文件夹。你不再需要 ZIP 文件了,所以你可以执行
并在提示符下输入“y”后按 Enter 键,以删除你下载的 ZIP 文件。
现在输入
然后按 Enter 键,将自己移动(即打开)到该目录中。你的提示符现在应该类似于下面的内容。
如果一切顺利,你应该执行
并看到一个名为 cash.c
的文件。执行 code cash.c
应该会打开该文件,你可以在其中输入此问题集的代码。如果不是,请回顾你的步骤,看看你哪里出错了!
贪心算法
在找零时,你很可能希望尽量减少为每位顾客提供的硬币数量,以免用完(或惹恼顾客!)。幸运的是,计算机科学为各地的收银员提供了减少应付硬币数量的方法:贪心算法。
根据美国国家标准与技术研究院 (NIST) 的说法,贪心算法是一种“在寻找答案时,总是采取最佳的、即时的或局部的解决方案的算法。贪心算法可以找到某些优化问题的整体或全局最优解,但对于其他问题的某些实例,可能会找到次优解。”
这到底是什么意思?假设收银员欠顾客一些零钱,而收银员的抽屉里有 25 美分的硬币 (quarter)、10 美分的硬币 (dime)、5 美分的镍币和 1 美分的便士。要解决的问题是决定给顾客哪些硬币以及每种硬币的数量。可以把这个收银员想象成总是想用最大面额的硬币来找零的人。例如,如果某个顾客欠 41 美分,那么可以采取的最大(即最佳的、即时的或局部的)第一步是使用一个 25 美分的硬币。(这一步之所以“最佳”,是因为它比任何其他硬币都能更快地让我们接近 0 美分。)请注意,这种大小的一步会将 41 美分的问题减少到 16 美分的问题,因为 41 - 25 = 16。也就是说,剩余部分是一个类似但更小的问题。不用说,再使用一个 25 美分的硬币就太大了(假设收银员不想赔钱),所以我们贪心的收银员会继续使用一个 10 美分的硬币,留下一个 6 美分的问题。此时,贪婪要求先使用一个 5 美分的硬币,然后再使用一个 1 美分的硬币,此时问题就解决了。顾客收到一个 25 美分的硬币、一个 10 美分的硬币、一个镍币和一个便士:总共四个硬币。
事实证明,这种贪心的方法(即算法)不仅是局部最优的,而且对于美国的货币(以及欧盟的货币)也是全局最优的。也就是说,只要收银员有足够的每种硬币,这种从大到小的方法将产生最少的硬币。有多少?嗯,你来告诉我们!
实现细节
在 cash.c
中,我们已经实现了一个程序的大部分(但不是全部!),该程序提示用户输入欠顾客的金额(以美分为单位),然后打印出可以用来找零的最小硬币数量。实际上,main
已经为你实现了。但是请注意,main
如何调用几个尚未实现的函数!其中一个函数 get_cents
不接受任何参数(如 void
所示),并返回一个 int
。其余函数都接受一个参数,一个 int
,并且也返回一个 int
。它们目前都返回 0
,以便代码可以编译。但是你需要用你自己的代码替换每个 TODO
和 return 0;
。具体实现方式如下:
- 实现
get_cents
,使其使用get_int
提示用户输入美分,然后将该数字作为int
返回。如果用户输入一个负数int
,你的代码应该再次提示用户。(但是你不需要担心用户输入,例如,一个string
,因为get_int
会为你处理。)你很可能会发现do while
循环很有帮助,就像mario.c
中一样! - 实现
calculate_quarters
,使其计算(并作为int
返回)如果欠顾客一定数量的美分,应该给顾客多少个 25 美分的硬币。例如,如果cents
是25
到49
之间的任何值,calculate_quarters
应该返回1
。如果cents
是50
到74
之间的任何值,calculate_quarters
应该返回2
。依此类推。 - 实现
calculate_dimes
,使其计算应该给顾客多少个 10 美分的硬币。 - 实现
calculate_nickels
,使其计算镍币的相同值。 - 实现
calculate_pennies
,使其计算便士的相同值。 请注意,与只有副作用的函数不同,返回值的函数应该使用return
语句显式返回值!请注意不要修改提供的原始代码,只需替换TODO
标记以及其后的return
语句返回值!另外,请记住抽象化的概念,每个calculate
函数都应该能处理任何cents
值,不应仅限于贪心算法可能产生的值。例如,如果cents
是85,那么calculate_dimes
函数应该返回8。
提示
- 回想一下,第 1 周的源代码中有几个示例程序,说明了函数如何返回值。
您的程序应按照以下示例运行。
$ ./cash
Change owed: 41
4
$ ./cash
Change owed: -41
Change owed: foo
Change owed: 41
4
如何测试你的代码
对于此程序,请尝试手动测试你的代码——这是一个很好的实践:
- 如果你输入
-1
,你的程序会再次提示你吗? - 如果你输入
0
,你的程序会输出0
吗? - 如果你输入
1
,你的程序会输出1
(即1美分)吗? - 如果你输入
4
,你的程序会输出4
(即4美分)吗? - 如果你输入
5
,你的程序会输出1
(即5美分)吗? - 如果你输入
24
,你的程序会输出6
(即2个一角硬币和4美分)吗? - 如果你输入
25
,你的程序会输出1
(即25美分硬币)吗? - 如果你输入
26
,你的程序会输出2
(即25美分硬币和1美分)吗? - 如果你输入
99
,你的程序会输出9
(即3个25美分硬币、2个一角硬币和4美分)吗?
你也可以执行以下命令,使用check50
来评估代码的正确性。但请务必先自行编译并测试!
check50 cs50/problems/2023/x/cash
check50
无法编译你的代码吗?
请确保你只修改了那些用TODO
标记的部分。例如,如果你修改了main
函数或者添加了全局变量,你的代码很可能无法通过编译。check50
将独立测试你的五个函数,而不仅仅是检查最终答案。
另外,执行以下命令可以使用style50
来评估你的代码风格。
如何提交
在你的终端中,执行以下命令来提交你的工作。
submit50 cs50/problems/2023/x/cash